package mgorm

import (
	"context"
	"fmt"
	"gitee.com/dreamwood/jxgo/v1/dev"
	"gitee.com/dreamwood/jxgo/v1/mgorm/expr"
	"gitee.com/dreamwood/jxgo/v1/ss"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"reflect"
	"strings"
)

type FilterBuilder struct {
	Config     *DocConfig
	FilterSelf ss.Obj
}

func NewFilter(config *DocConfig) *FilterBuilder {
	fb := &FilterBuilder{
		Config: config,
	}
	return fb
}

func (fb *FilterBuilder) CreateFilterFromQuery(query *Query) bson.D {
	obj, e := ss.AssertObj(query.Filters)
	if e != nil {
		return bson.D{{Key: "_id", Value: false}}
	}
	return fb.handleMap(obj, fb.Config)
}

func (fb *FilterBuilder) handleMap(obj ss.Obj, config *DocConfig) bson.D {
	data := make(bson.D, 0)
	for key, value := range obj {
		data = append(data, fb.handleKeyValue(key, value, config))
	}
	return data
}

func (fb *FilterBuilder) handleArrayMap(obj []ss.Obj, config *DocConfig) bson.D {
	data := make(bson.D, 0)
	for _, value := range obj {
		data = append(data, fb.handleMap(value, config)[0])
	}
	return data
}

func (fb *FilterBuilder) handleKeyValue(key string, value interface{}, config *DocConfig) bson.E {
	switch key {
	case "_and":
		v, e := ss.AssertArrayObj(value)
		if e != nil {
			//断言失败说明参数不合法 todo 直接抛弃
			return bson.E{Key: "_id", Value: false}
		}
		return bson.E{Key: expr.AND_C, Value: bson.A{fb.handleArrayMap(v, config)}}
	case "_or":
		v, e := ss.AssertArrayObj(value)
		if e != nil {
			//断言失败说明参数不合法 todo 直接抛弃
			return bson.E{Key: "_id", Value: false}
		}
		ors := make(bson.A, len(v))
		for i := range v {
			ors[i] = fb.handleMap(v[i], config)
		}
		return bson.E{Key: expr.OR_C, Value: ors}
	case "_not":
		v, e := ss.AssertObj(value)
		if e != nil {
			return bson.E{Key: "_id", Value: false}
		}
		return bson.E{Key: expr.NOT_C, Value: fb.handleMap(v, config)}
	default:
		return fb.handleNormalKeyAndValue(key, value, config)
	}
}

func (fb *FilterBuilder) handleNormalKeyAndValue(key string, val interface{}, config *DocConfig) bson.E {
	//"xx":v
	//"xx__expr":v
	//"xx.xx":v
	//"xx.xx_expr":v
	//"xx.xx.xx":v
	//"xx.xx.xx_expr":v
	//"cat.tag._id": "68876d927d275213dcdfe621",len = 3
	//"cat.name__regex": "AAA",len = 3
	//分两种情况，一种是不跨表的，一种是跨表（包含点"."）
	//输出Doc的反射
	doc := config.GetFactory().Doc
	tp := reflect.TypeOf(doc)
	dev.PrintDebug(tp.String())

	dotSplit := strings.SplitN(key, ".", 2)
	switch len(dotSplit) {
	case 1:
		//再判断一下是否包含表达式
		//_id__gt:"1"
		keyAndExpr := strings.SplitN(key, "__", 2)
		switch len(keyAndExpr) {
		case 1: //单纯筛选
			return bson.E{Key: key, Value: fb.transformValueByKey(key, val)}
		case 2: //带表达式的筛选
			return bson.E{Key: keyAndExpr[0], Value: fb.transformExpr(keyAndExpr[1], fb.transformValueByKey(keyAndExpr[0], val))}
		default:
			return bson.E{Key: key, Value: fb.transformValueByKey(key, val)}
		}
	case 2:
		localKey := dotSplit[0]
		otherKey := dotSplit[1]
		relation := config.RelationConfigs[localKey]
		ids := fb.getIdsFromLinkedKeyAndValue(otherKey, val, relation.Config())
		k := ""
		if relation.JoinType == "O" {
			//对一的关系，
			k = fmt.Sprintf("%sId", localKey)
		}
		if relation.JoinType == "M" {
			//对多的关系，
			k = fmt.Sprintf("%sIds", localKey)
		}
		return bson.E{Key: k, Value: bson.D{
			{Key: expr.IN_C, Value: ids},
		}}
	}
	return bson.E{}
}

func (fb *FilterBuilder) getIdsFromLinkedKeyAndValue(key string, val interface{}, config *DocConfig) []primitive.ObjectID {
	ids := make([]primitive.ObjectID, 0)
	dotSplit := strings.SplitN(key, ".", 2)
	switch len(dotSplit) {
	case 1:
		factory := config.GetFactory()
		docs := make([]SimpleDoc, 0)
		tmp := strings.SplitN(key, "__", 2)
		if len(tmp) == 2 {
			key = tmp[0]
			val = fb.transformExpr(tmp[1], fb.transformValueByKey(key, val))
		}
		c, e := factory.Coll().Find(context.TODO(), bson.D{{key, fb.transformValueByKey(key, val)}})
		if e != nil {
			dev.PrintDebug(e.Error())
			return nil
		}
		err := c.All(context.TODO(), &docs)
		if err == nil {
			for i := range docs {
				ids = append(ids, docs[i].Id)
			}
		} else {
			dev.PrintDebug(err.Error())
		}
		return ids
	case 2:
		localKey := dotSplit[0]
		otherKey := dotSplit[1]
		relation := config.RelationConfigs[localKey]
		inIds := fb.getIdsFromLinkedKeyAndValue(otherKey, val, relation.Config())
		if relation.JoinType == "O" {
			//对一的关系，
			//1.M20,多对一，关联关系维持在M表,查询字段为relation.KeyInside
			//2.O2O反向一对一，暂时不支持//todo
			return fb.getIdsFromLinkedKeyAndValue(fmt.Sprintf("%sId__in", localKey), inIds, config)
		}
		if relation.JoinType == "M" {
			//对多的关系，
			return fb.getIdsFromLinkedKeyAndValue(fmt.Sprintf("%sIds__in", localKey), inIds, config)
		}
	}
	return ids
}

func (fb *FilterBuilder) transformExpr(expr string, val interface{}) bson.D {
	switch expr {
	case "ina":
	default:
		return bson.D{bson.E{fmt.Sprintf("$%s", expr), val}}
	}
	return bson.D{}
}

func (fb *FilterBuilder) transformValueByKey(key string, val interface{}) interface{} {
	if key == "_id" {
		dev.PrintDebugGo(val)
		id, ok := val.(string)
		if ok {
			return fb.makeId(id)
		}
		//
		//switch val.(type) {
		//case string:
		//	return fb.makeId(val)
		//default:
		//	return val
		//}
		//return fb.makeId(val)
	}
	return val
}

func (fb *FilterBuilder) makeId(val interface{}) interface{} {
	switch val.(type) {
	case string:
		id, _ := primitive.ObjectIDFromHex(val.(string))
		return id
	}
	zero, _ := primitive.ObjectIDFromHex("")
	return zero
}
